// eee.Sheffield.PZ.Imaging
//
// Copyright ?Ping Zou, 2007
// sg71.cherub@gmail.com

using System;
using System.Collections.Generic;
using System.Text;
using eee.Sheffield.PZ.Math;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace eee.Sheffield.PZ.Imaging.ImageFilter
{
    public class FreeEndDetector : ImageFilter//, IGraylevelImageFilter
    {
        #region Fields
        private List<bool[,]> _kernelType1List; // one and only one neighbourhood * 8
        private List<bool[,]> _kernelType2List; // L shape ends * 8
        private List<bool[,]> _kernelType3List; // T shape ends * 8
        private List<PZPoint> _freeEndList;  // free end points list
        #endregion

        #region Fields
        public List<PZPoint> FreeEndList { get { return _freeEndList; } }
        #endregion

        #region Constructor
        public FreeEndDetector()
        {
            PrepareKernels();
            _flag = "[Image -> Morphology -> Free End Detector]";
        }
        #endregion

        #region Apply filter
        protected override PZMath_matrix ApplyDoubleGraylevelImageFilter(PZMath_matrix srcMatrix, out string logText)
        {
            // AndTrue operations with 16 kernels
            List<PZMath_matrix> dstMatrixList = new List<PZMath_matrix>(20);
            for (int i = 0; i < 8; i++)
                dstMatrixList.Add(srcMatrix.AndTrueFalse2D((bool[,])_kernelType1List[i]));
            for (int i = 0; i < 8; i ++)
                dstMatrixList.Add(srcMatrix.AndTrueFalse2D((bool[,])_kernelType2List[i]));
            for (int i = 0; i < 4; i ++)
                dstMatrixList.Add(srcMatrix.AndTrueFalse2D((bool[,])_kernelType3List[i]));

            // OR all dstMatrixs
            PZMath_matrix dstMatrix = new PZMath_matrix((PZMath_matrix)dstMatrixList[0]);
            int height = dstMatrix.RowCount;
            int width = dstMatrix.ColumnCount;
            double temp;
            for (int i = 1; i < 20; i++)
            // for each AndTrue output
            {
                for (int y = 0; y < height; y++)
                {
                    for (int x = 0; x < width; x++)
                    {

                        temp = ((PZMath_matrix)dstMatrixList[i])[y, x];
                        dstMatrix[y, x] = dstMatrix[y, x] < temp ?
                            dstMatrix[y, x] : temp;     // OR operation
                    }
                }
            }

            _freeEndList = new List<PZPoint>(0);
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    if (dstMatrix[y, x] == 0)
                        _freeEndList.Add(new PZPoint(x, y));
                }
            }

            logText = "succeeds.";
            return dstMatrix;
        } // ApplyDoubleGraylevelImageFilter()

        protected override PZMath_matrix[] ApplyDoubleColourImageFilter(PZMath_matrix[] srcMatrixArray, out string logText)
        {
            // TODO: add this function
            throw new Exception("The method or operation is not implemented.");
        } // ApplyDoubleColourImageFilter()
        #endregion

        #region prepare kernel
        /// <summary>
        /// prepare kernels
        /// </summary>
        private void PrepareKernels()
        {
            _kernelType1List = new List<bool[,]>(8);

            // have one and only one neighbour * 8 
            bool[,] kernel1 = new bool[,] 
            {
                {false, false, true},
                {false, true, false},
                {false, false, false}
            };
            _kernelType1List.Add(kernel1);

            bool[,] kernel2 = new bool[,]
            {
                {false, false, false},
                {false, true, true},
                {false, false, false}
            };
            _kernelType1List.Add(kernel2);

            bool[,] kernel3 = new bool[,]
            {
                {false, false, false},
                {false, true, false},
                {false, false, true}
            };
            _kernelType1List.Add(kernel3);

            bool[,] kernel4 = new bool[,]
            {
                {false, false, false},
                {false, true, false},
                {false, true, false}
            };
            _kernelType1List.Add(kernel4);

            bool[,] kernel5 = new bool[,]
            {
                {false, false, false},
                {false, true, false},
                {true, false, false}
            };
            _kernelType1List.Add(kernel5);

            bool[,] kernel6 = new bool[,]
            {
                {false, false, false},
                {true, true, false},
                {false, false, false}
            };
            _kernelType1List.Add(kernel6);

            bool[,] kernel7 = new bool[,]
            {
                {true, false, false},
                {false, true, false},
                {false, false, false}
            };
            _kernelType1List.Add(kernel7);

            bool[,] kernel8 = new bool[,]
            {
                {false, true, false},
                {false, true, false},
                {false, false, false}
            };
            _kernelType1List.Add(kernel8);

            _kernelType2List = new List<bool[,]>(8);
            // L shape ends, * 8
            bool[,] kernel9 = new bool[,] 
            {
                {false, false, true},
                {false, true, true},
                {false, false, false}
            };
            _kernelType2List.Add(kernel9);

            bool[,] kernel10 = new bool[,]
            {
                {false, false, false},
                {false, true, true},
                {false, false, true}
            };
            _kernelType2List.Add(kernel10);

            bool[,] kernel11 = new bool[,]
            {
                {false, false, false},
                {false, true, false},
                {false, true, true}
            };
            _kernelType2List.Add(kernel11);

            bool[,] kernel12 = new bool[,]
            {
                {false, false, false},
                {false, true, false},
                {true, true, false}
            };
            _kernelType2List.Add(kernel12);

            bool[,] kernel13 = new bool[,]
            {
                {false, false, false},
                {true, true, false},
                {true, false, false}
            };
            _kernelType2List.Add(kernel13);

            bool[,] kernel14 = new bool[,]
            {
                {true, false, false},
                {true, true, false},
                {false, false, false}
            };
            _kernelType2List.Add(kernel14);

            bool[,] kernel15 = new bool[,]
            {
                {true, true, false},
                {false, true, false},
                {false, false, false}
            };
            _kernelType2List.Add(kernel15);

            bool[,] kernel16 = new bool[,]
            {
                {false, true, true},
                {false, true, false},
                {false, false, false}
            };
            _kernelType2List.Add(kernel16);

            _kernelType3List = new List<bool[,]>(4);
            // T shape ends, * 8
            bool[,] kernel17 = new bool[,] 
            {
                {true, true, true},
                {false, true, false},
                {false, false, false}
            };
            _kernelType3List.Add(kernel17);

            bool[,] kernel18 = new bool[,]
            {
                {false, false, true},
                {false, true, true},
                {false, false, true}
            };
            _kernelType3List.Add(kernel18);

            bool[,] kernel19 = new bool[,]
            {
                {false, false, false},
                {false, true, false},
                {true, true, true}
            };
            _kernelType3List.Add(kernel19);

            bool[,] kernel20 = new bool[,]
            {
                {true, false, false},
                {true, true, false},
                {true, false, false}
            };
            _kernelType3List.Add(kernel20);
        } // PrepareKernels()
        #endregion

        #region I/O method
        /// <summary>
        /// return a free end image
        /// free ends are rendered in red, overlapping the srcImage
        /// </summary>
        /// <param name="srcImage"></param>
        /// <returns></returns>
        public Bitmap FreeEndImage(Bitmap srcImage)
        {
            Bitmap dstImage = new Bitmap(srcImage);
            int junctionCount = _freeEndList.Count;
            for (int i = 0; i < junctionCount; i++)
            {
                PZPoint p = (PZPoint)_freeEndList[i];
                dstImage.SetPixel((int)p.x, (int)p.y, Color.Red);
            }

            return dstImage;
        } // FreeEndImage()

        /// <summary>
        /// write file
        /// </summary>
        /// <param name="fileName"></param>
        public void WriteFile(string fileName)
        {
            FileStream fileStream = new FileStream(fileName, FileMode.Create, FileAccess.Write);
            StreamWriter streamWriter = new StreamWriter(fileStream);
            foreach (PZPoint p in _freeEndList)
                streamWriter.WriteLine(p.ToString());
            streamWriter.Flush();
            streamWriter.Close();
            fileStream.Close();
        } // WriteFile()

        /// <summary>
        /// read file
        /// </summary>
        /// <param name="fileName"></param>
        public void ReadFile(string fileName)
        {
            _freeEndList = new List<PZPoint>(0);

            FileStream fileStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
            StreamReader streamReader = new StreamReader(fileStream);
            while (!streamReader.EndOfStream)
            {
                string s = streamReader.ReadLine();
                int midIndex = s.IndexOf(',');
                double x = Convert.ToDouble(s.Substring(1, midIndex - 1));
                double y = Convert.ToDouble(s.Substring(midIndex + 1, s.Length - midIndex - 2));
                _freeEndList.Add(new PZPoint(x, y));
            }
            streamReader.Close();
            fileStream.Close();
        } // ReadFile()

        #endregion
    }
}
